<?php
// +----------------------------------------------------------------------
// | QSCMS
// +----------------------------------------------------------------------
// | Copyright (c)  2025 https://www.qqss.net All rights reserved.
// +----------------------------------------------------------------------
// | Licensed QSCMS is not free software, commercial use must purchase official license from official website to avoid unnecessary legal disputes.
// +----------------------------------------------------------------------
// | Author: Contract 990504246@qq.com
// +----------------------------------------------------------------------

namespace app\common\plugin;

/**
 * 钩子注册表 - 实现零I/O开销的钩子系统
 * 全局钩子存储机制
 */
class HookRegistry
{
    /**
     * 全局钩子存储
     * 格式: [event_name => [priority => [callbacks...]]]
     */
    private static $hooks = [];

    /**
     * 是否已初始化
     */
    private static $initialized = false;

    /**
     * 初始化钩子注册表
     * 在应用启动时调用，预加载所有钩子到内存
     */
    public static function initialize()
    {
        if (self::$initialized) {
            return;
        }

        self::loadAllHooks();
        self::$initialized = true;
    }

    /**
     * 预加载所有启用插件的钩子
     */
    private static function loadAllHooks()
    {
        $enabled_plugins = get_enabled_plugins();

        foreach ($enabled_plugins as $plugin_name) {
            self::loadPluginHooks($plugin_name);
        }
    }

    /**
     * 加载单个插件的钩子
     */
    private static function loadPluginHooks($plugin_name)
    {
        $plugin_path = get_plugin_path($plugin_name);
        $events_file = $plugin_path . '/events.php';

        if (!file_exists($events_file)) {
            return;
        }

        // 读取禁用的钩子列表
        $disabled_hooks_file = $plugin_path . '/disabled_hooks.php';
        $disabled_hooks      = [];
        if (file_exists($disabled_hooks_file)) {
            try {
                $disabled_hooks = require $disabled_hooks_file;
                if (!is_array($disabled_hooks)) {
                    $disabled_hooks = [];
                }
            } catch (\Exception $e) {
                // error_log("读取禁用钩子列表失败 [{$plugin_name}]: " . $e->getMessage());
                $disabled_hooks = [];
            }
        }

        try {
            $plugin_events = require $events_file;

            foreach ($plugin_events as $event_name => $listeners) {
                // 检查钩子是否被禁用
                if (in_array($event_name, $disabled_hooks)) {
                    // error_log("跳过禁用的钩子: {$plugin_name} -> {$event_name}");
                    continue;
                }

                // 确保监听器是数组格式
                if (!is_array($listeners[0])) {
                    $listeners = [$listeners];
                }

                foreach ($listeners as $listener) {
                    if (is_array($listener) && count($listener) >= 2) {
                        $class_name  = $listener[0];
                        $method_name = $listener[1];
                        $priority    = $listener[2] ?? 10;

                        self::addHook($event_name, [
                            'plugin'   => $plugin_name,
                            'class'    => $class_name,
                            'method'   => $method_name,
                            'priority' => $priority
                        ]);
                    }
                }
            }
        } catch (\Exception $e) {
            // error_log("加载插件钩子失败 [{$plugin_name}]: " . $e->getMessage());
        }
    }

    /**
     * 添加钩子到注册表
     */
    public static function addHook($event_name, $callback)
    {
        $priority = $callback['priority'] ?? 10;

        if (!isset(self::$hooks[$event_name])) {
            self::$hooks[$event_name] = [];
        }

        if (!isset(self::$hooks[$event_name][$priority])) {
            self::$hooks[$event_name][$priority] = [];
        }

        self::$hooks[$event_name][$priority][] = $callback;

        // 保持优先级排序
        ksort(self::$hooks[$event_name]);
    }

    /**
     * 获取事件的所有监听器
     * 零I/O开销 - 直接从内存读取
     */
    public static function getHooks($event_name)
    {
        if (!isset(self::$hooks[$event_name])) {
            return [];
        }

        $listeners = [];
        foreach (self::$hooks[$event_name] as $priority => $callbacks) {
            foreach ($callbacks as $callback) {
                $listeners[] = $callback;
            }
        }

        return $listeners;
    }

    /**
     * 执行钩子
     * 零I/O开销的核心实现
     */
    public static function executeHook($event_name, $params = null)
    {
        $result    = $params;
        $listeners = self::getHooks($event_name);

        foreach ($listeners as $listener) {

            $plugin_name = $listener['plugin'];
            $class_name  = $listener['class'];
            $method_name = $listener['method'];

            // 获取插件实例（带缓存）
            $instance = self::getInstance($plugin_name, $class_name);

            if ($instance && method_exists($instance, $method_name)) {
                $plugin_result = $instance->$method_name($result);

                // 合并结果
                if ($plugin_result !== null && $plugin_result !== false) {
                    if (is_string($result) && is_string($plugin_result)) {
                        $result .= $plugin_result;
                    } else {
                        $result = $plugin_result;
                    }
                }
            }
        }

        return $result;
    }

    /**
     * 获取插件实例（动态创建，避免静态缓存导致内存泄露）
     * 按照官方建议：不使用单例，每次用new关键字初始化
     */
    private static function getInstance($plugin_name, $class_name)
    {
        if (class_exists($class_name)) {
            try {
                // 每次动态创建新实例，避免静态缓存膨胀
                return new $class_name($plugin_name);
            } catch (\Exception $e) {
                error_log("Failed to create plugin instance [{$plugin_name}::{$class_name}]: " . $e->getMessage());
                return null;
            }
        }

        return null;
    }

    /**
     * 重新加载钩子注册表
     * 插件启用/禁用时调用
     * 修复内存泄露：移除静态实例缓存
     */
    public static function reload()
    {
        // 只清空钩子数组，不再维护实例缓存
        self::$hooks       = [];
        self::$initialized = false;

        // 强制垃圾回收
        if (function_exists('gc_collect_cycles')) {
            gc_collect_cycles();
        }

        self::initialize();
    }

    /**
     * 获取所有钩子信息（调试用）
     */
    public static function getAllHooks()
    {
        return self::$hooks;
    }

    /**
     * 获取钩子统计信息
     */
    public static function getStats()
    {
        $total_events    = count(self::$hooks);
        $total_listeners = 0;

        foreach (self::$hooks as $priorities) {
            foreach ($priorities as $callbacks) {
                $total_listeners += count($callbacks);
            }
        }

        return [
            'total_events'     => $total_events,
            'total_listeners'  => $total_listeners,
            'memory_usage'     => memory_get_usage(true),
            'memory_formatted' => self::formatBytes(memory_get_usage(true)),
            'initialized'      => self::$initialized,
            'note'             => 'Instance cache removed to prevent memory leaks'
        ];
    }

    /**
     * 格式化字节数为可读格式
     */
    private static function formatBytes($bytes)
    {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $bytes = max($bytes, 0);
        $pow   = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow   = min($pow, count($units) - 1);

        $bytes /= pow(1024, $pow);

        return round($bytes, 2) . ' ' . $units[$pow];
    }
}
